#!/usr/bin/env python

"""
A simple script to make booklets by reordering other PDF pages.
"""

__author__ = "Pedro Chambino"
__author_email__ = "pchambino@gmail.com"

_name_ = "bookletMaker"
_version_ = 1.4
_debug_ = False

from sys import exit, stderr
from getopt import getopt, GetoptError
import sys, os.path

def version_and_exit():
    print "Version: %s" % _version_
    exit(0)

def usage():
    return "Usage: %s [options] <input-pdf-file>\n" \
           "        Makes a booklet from the input PDF file by reordering its pages.\n" \
           "\n" \
           "        Options:\n" \
           "            -d  --debug             Prints debug messages.\n" \
           "            -s  --size=<size>       Splits into booklets of the defined size.\n" \
           "            -o  --output=<file>     Define the output file.\n" \
           "            -f  --force             Overrides output file if necessary.\n" \
           "            -r  --range=<form>:<to> Selects range to make booklets.\n" \
           "                --from=<n>          Lower bound value for range (see --range).\n" \
           "                --to=<n>            Upper bound value for range (see --range).\n" \
           "\n" \
           "       %s [-h|--help]\n" \
           "            Prints this text.\n" \
           "\n" \
           "       %s [-v|--version]\n" \
           "            Prints version.\n" \
           % (_name_, _name_, _name_)

def usage_and_exit():
    print usage()
    exit(0)

def debug(msg):
    if _debug_:
        stderr.write("DEBUG: "+msg+"\n")

def debug_and_exit(msg, code):
    debug(msg)
    exit(code)
    
def error_and_exit(msg, code):
    stderr.write(msg)
    stderr.write("\nType '%s -h' for help.\n" % _name_)
    exit(code)

def validate_and_convert_to_int(name, value):
    try:
        return int(value)
    except ValueError:
        error_and_exit("%s value must be an integer: %s" % (name,value), 1)

def main(argv):
    try:
        opts, args = getopt(argv, "hvds:fo:r:", 
        ["help", "version", "debug", "size=", "force", 
        "output=", "range=", "from=", "to="])
    except GetoptError, err:
        error_and_exit(err.msg, 1)

    info = BookletMakerInfo()

    if args:
        info.inputFile = args[0]

    for opt, arg in opts:
        if opt in ("-h", "--help"):
            usage_and_exit()
        if opt in ("-v", "--version"):
            version_and_exit()
        elif opt in ("-d", "--debug"):
            global _debug_
            _debug_ = True
        elif opt in ("-s", "--size"):
            info.maxSize = validate_and_convert_to_int("--size", arg)
        elif opt in ("-f", "--force"):
            info.force = True
        elif opt in ("-o", "--output"):
            info.outputFile = arg
        elif opt in ("-r", "--range"):
            try:
                (info.fromPage, info.toPage) = arg.split(":")
            except ValueError:
                error_and_exit("--range value must be in format: <from>:<to>", 1)
            info.fromPage = validate_and_convert_to_int("--range <from>", info.fromPage) - 1
            info.toPage = validate_and_convert_to_int("--range <to>", info.toPage) - 1
        elif opt in ("--from"):
            info.fromPage = validate_and_convert_to_int("--from", arg) - 1
        elif opt in ("--to"):
            info.toPage = validate_and_convert_to_int("--to", arg) - 1
        else:
            debug("Forgotten option: %s" % opt)
            
    info.validate().make_booklets().write_booklets()


class BookletMakerInfo:

    def __init__(self):
        self.inputFile = None
        self.outputFile = None
        self.force = False
        self.maxSize = None
        self.fromPage = 0
        self.toPage = None
        
    def validate(self):
        try:
            from pyPdf import PdfFileReader, PdfFileWriter
            from pyPdf.utils import PdfReadError
        except ImportError:
            error_and_exit("Could NOT find pyPdf. Run: easy_install pyPdf", 1)
    
        if self.maxSize and self.maxSize % 4:
            error_and_exit("--size value must be multiple of 4: %s"
                           % self.maxSize, 1)

        if not self.inputFile:
            error_and_exit("No input file set.", 1)
        if not os.path.isfile(self.inputFile):
            error_and_exit("Invalid file: %s" % self.inputFile, 1)
    
        if not self.outputFile:
            basename, extention = os.path.splitext(self.inputFile)
            self.outputFile = basename+" booklet"+extention
        if not self.force and os.path.exists(self.outputFile):
            error_and_exit("Output file already exists: %s\n"
                           "Use the --force option to override."
                           % self.outputFile, 1)
    
        if self.inputFile == self.outputFile:
            error_and_exit("The input and output file can NOT be the same: %s"
                           % self.inputFile, 1)
    
        inputStream = open(self.inputFile, "rb")
        
        try:
            input = PdfFileReader(inputStream)
        except PdfReadError:
            error_and_exit("Input file is not a PDF file: %s" % self.inputFile, 1)
        
        debug("File pages count: %s" % input.numPages)
    
        if not self.toPage:
            self.toPage = input.numPages - 1
        elif self.toPage > input.numPages - 1:
            error_and_exit("--to value must not be greater than file pages: %s < %s"
                           % (self.toPage, input.numPages) , 1)
    
        if self.fromPage > self.toPage:
            error_and_exit("--from value must not be greater than --to value: %s < %s"
                           % (self.fromPage, self.toPage), 1)
    
        pagesCount = self.toPage - self.fromPage + 1
    
        if pagesCount % 4:
            error_and_exit("Pages count should be multiple of 4: %s"
                           % pagesCount, 1)
    
        if not self.maxSize:
            self.maxSize = pagesCount
            
        outputStream = open(self.outputFile, "wb")
        output = PdfFileWriter()
        
        maker = BookletMaker()
        maker.__dict__.update(self.__dict__)
        maker.inputStream = inputStream
        maker.outputStream = outputStream
        maker.input = input
        maker.output = output
        
        return maker


class BookletMaker(BookletMakerInfo):
    
    def __init__(self):
        BookletMakerInfo.__init__(self)
        self.inputStream = None
        self.outputStream = None
        self.input = None
        self.output = None

    def write_booklets(self):
        debug("Writing...")
        self.output.write(self.outputStream)
        self.outputStream.close()

    def make_booklets(self):
        debug("Page range: %s-%s" % (self.fromPage, self.toPage))
        debug("Max booklets size: %s" % self.maxSize)
    
        i = self.fromPage
        j = i+self.maxSize-1
        
        while i <= self.toPage:
            if j > self.toPage:
                j = self.toPage
    
            self.__make_booklet(i, j)
            i += self.maxSize
            j += self.maxSize
            
        return self
    
    def __make_booklet(self, i, j):
        debug("Making booklet from %s to %s." % (i, j))
        
        while i < j:
            debug("Pages %s %s %s %s" % (j, i, i+1, j-1))
            self.output.addPage(self.input.getPage(j))
            self.output.addPage(self.input.getPage(i))
            j-=1
            i+=1
            self.output.addPage(self.input.getPage(i))
            self.output.addPage(self.input.getPage(j))
            j-=1
            i+=1


if __name__ == "__main__":
    main(sys.argv[1:])
    exit(0)
